// All contents of this file written by Colin Fahey ( http://colinfahey.com )
// 2007 June 4 ; Visit web site to check for any updates to this file.



using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Windows.Forms;



namespace CPF.StandardTetris
{
    public class STStrategyUserDefined : STStrategy
    {

        public ushort[] Constatnts;
        public override String GetStrategyName ( )
        {
            return ("User Defined");
        }
      


        // WARNING: Moves requiring rotation must wait until piece has fallen by
        // at least one row.

        public override void GetBestMoveOncePerPiece
        (
            STBoard board,
            STPiece piece,
            bool nextPieceFlag, // false == no next piece available or known
            STPiece.STPieceShape nextPieceShape, // None == no piece available or known
            ref int bestRotationDelta, // 0 or {0,1,2,3}
            ref int bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...}
        )
        {
            bestRotationDelta = 0;
            bestTranslationDelta = 0;

            // We are given the current board, and the current piece
            // configuration.  Our goal is to evaluate various possible
            // moves and return the best move we explored.

            PrivateStrategy
            (
                false,
                board,
                piece,
                ref bestRotationDelta, // 0 or {0,1,2,3}
                ref bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...}
            );
        }





        private double PrivateStrategy
        (
            bool flagCalledFromParentPly,  // True if called from a parent level
            STBoard board,
            STPiece piece,
            ref int bestRotationDelta,    // 0 or {0,1,2,3} 
            ref int bestTranslationDelta  // 0 or {...,-2,-1,0,1,2,...}
        )
        {
           
            


            int currentBestTranslationDelta = 0;
            int currentBestRotationDelta = 0;
            double currentBestMerit = (-1.0e+20); // Really bad!

            int trialTranslationDelta = 0;
            int trialRotationDelta = 0;
            double trialMerit = 0.0;

            bool moveAcceptable = false;
            int count = 0;


            STBoard tempBoard = new STBoard( );
            STPiece tempPiece = new STPiece( );



            int maxOrientations = 0;
            maxOrientations = STPiece.GetMaximumOrientationsOfShape( piece.GetShape( ) );

        
            for(trialRotationDelta = 0;trialRotationDelta < maxOrientations;trialRotationDelta++)
            {
                // Make temporary copy of piece, and rotate the copy.
                tempPiece.CopyFrom( piece );
                for (count = 0; count < trialRotationDelta; count++)
                {
                    tempPiece.Rotate( );
                }
                // Determine the translation limits for this rotated piece.
                bool moveIsPossible = false;
                int minDeltaX = 0;
                int maxDeltaX = 0;

                board.DetermineAccessibleTranslationsForPieceOrientation( tempPiece,ref moveIsPossible,ref minDeltaX, ref maxDeltaX);
                // right limit
                // left limit

                // Consider all allowed translations for the current rotation.
                if (true == moveIsPossible)
                {
                    for( trialTranslationDelta = minDeltaX;trialTranslationDelta <= maxDeltaX;trialTranslationDelta++)
                    {
                        // Evaluate this move

                        // Copy piece to temp and rotate and translate
                        tempPiece.CopyFrom( piece );
                        for (count = 0; count < trialRotationDelta; count++)
                        {
                            tempPiece.Rotate( );
                        }
                        tempPiece.Translate( trialTranslationDelta, 0 );

                        moveAcceptable =board.DetermineIfPieceIsWithinBoardAndDoesNotOverlapOccupiedCells(tempPiece );
                       
                        
                        if (true == moveAcceptable)
                        {
                            //int temp_x = tempPiece.GetX();
                            //int temp_y = tempPiece.GetY();
                            // Because the piece can BE (not necessarily GET) at the goal
                            // horizontal translation and orientation, it's worth trying
                            // out a drop and evaluating the move.
                            tempBoard.CopyFrom( board );
                            //temp_x += temp_y;


                            tempBoard.FullDropAndCommitPieceToBoard( tempPiece );

                            // Pierre Dellacherie (France) Board & Piece Evaluation Function
                            this.PrivateStrategyEvaluate(tempBoard,tempPiece, ref trialMerit);

                            // If this move is better than any move considered before,
                            // or if this move is equally ranked but has a higher priority,
                            // then update this to be our best move.
                            if (trialMerit > currentBestMerit)
                            {
                                currentBestMerit = trialMerit;
                                currentBestTranslationDelta = trialTranslationDelta;
                                currentBestRotationDelta = trialRotationDelta;
                            }
                        }
                    }
                }
            }


            // commit to this move
            bestTranslationDelta = currentBestTranslationDelta;
            bestRotationDelta = currentBestRotationDelta;

            return (currentBestMerit);
        }





        void PrivateStrategyEvaluate( STBoard board,STPiece piece,ref double rating)
        {
            rating = 0.0;
            int[] lineCellTotal = new int[(1 + 20 + 200)];
          
            int width = 0;
            int height = 0;
            int clears = 0;
            
           
            width = board.GetWidth();
            height = board.GetHeight();
            
          

            if (false == piece.IsValid( ))
            {
                return;
            }

            for (int y = 1; y <= height; y++)
            {
                lineCellTotal[(y - 1)] = 0;
                for (int x = 1; x <= width; x++)
                {
                    if (board.GetCell(x, y) > 0)
                        lineCellTotal[(y - 1)]++;

                }
            }

            
            int totalCells = 0;
            int wallTouch = 0;
            int bottomTouch = 0;
            int blockTouch = 0;
            totalCells = piece.GetTotalCells();
            int[,] cellsCoordinates = new int[totalCells,2];

            int cellIndex = 0;
            for (cellIndex = 1; cellIndex <= totalCells; cellIndex++)
            {
                int boardX = 0;
                int boardY = 0;
                piece.GetTranslatedCellXY(cellIndex, ref boardX, ref boardY);
                cellsCoordinates[cellIndex -1,0] = boardX;
                cellsCoordinates[cellIndex -1,1] = boardY;
            }
            List<int[]> lst = new List<int[]>();
            for (int i = 0; i < totalCells; i++)
            {
                int current_x = cellsCoordinates[i, 0];
                int current_y = cellsCoordinates[i, 1];
                if (current_x - 1 == 0)
                    wallTouch++;
                else if (current_x + 1 == board.GetWidth() + 1)
                    wallTouch++;

                if (current_y - 1 == 0)
                    bottomTouch++;
                
                int c_x = current_x - 1;
                int c_y = current_y + 1;
                if (board.GetCell(c_x, c_y) != (byte)0)
                    if (isForeignBlock(c_x, c_y, cellsCoordinates) && (lst.FirstOrDefault(e => (e[0] == c_x && e[1] == c_y)) == null))
                    {
                        lst.Add(new int[] { c_x, c_y });
                        blockTouch++;
                    }
                c_x = current_x + 1;
                c_y = current_y + 1;
                if (board.GetCell(c_x, c_y) != (byte)0)
                    if (isForeignBlock(c_x, c_y, cellsCoordinates) && (lst.FirstOrDefault(e => (e[0] == c_x && e[1] == c_y)) == null))
                    {
                        lst.Add(new int[] { c_x, c_y });
                        blockTouch++;
                    }
                c_x = current_x - 1;
                c_y = current_y;
                if (board.GetCell(c_x, c_y) != (byte)0)
                    if (isForeignBlock(c_x, c_y, cellsCoordinates) && (lst.FirstOrDefault(e => (e[0] == c_x && e[1] == c_y)) == null))
                    {
                        lst.Add(new int[] { c_x, c_y });
                        blockTouch++;
                    }
                c_x = current_x + 1;
                c_y = current_y;
                if (board.GetCell(c_x, c_y) != (byte)0)
                    if (isForeignBlock(c_x, c_y, cellsCoordinates) && (lst.FirstOrDefault(e => (e[0] == c_x && e[1] == c_y)) == null))
                    {
                        lst.Add(new int[] { c_x, c_y });
                        blockTouch++;
                    }
                c_x = current_x - 1;
                c_y = current_y - 1;
                if (board.GetCell(c_x, c_y) != (byte)0)
                    if (isForeignBlock(c_x, c_y, cellsCoordinates) && (lst.FirstOrDefault(e => (e[0] == c_x && e[1] == c_y)) == null))
                    {
                        lst.Add(new int[] { c_x, c_y });
                        blockTouch++;
                    }
                c_x = current_x;
                c_y = current_y - 1;
                if (board.GetCell(c_x, c_y) != (byte)0)
                    if (isForeignBlock(c_x, c_y, cellsCoordinates) && (lst.FirstOrDefault(e => (e[0] == c_x && e[1] == c_y)) == null))
                    {
                        lst.Add(new int[] { c_x, c_y });
                        blockTouch++;
                    }
                c_x = current_x + 1;
                c_y = current_y - 1;
                if (board.GetCell(c_x, c_y) != (byte)0)
                    if (isForeignBlock(c_x, c_y, cellsCoordinates) && (lst.FirstOrDefault(e => (e[0] == c_x && e[1] == c_y)) == null))
                    {
                        lst.Add(new int[] { c_x, c_y });
                        blockTouch++;
                    }
            }
           
            int pileHeight = 0;

            for (int y = height; y >= 1; y--)
            {
                for (int x = 1; x <= width; x++)
                {
                    byte cellValue = (byte)0;
                    cellValue = board.GetCell(x, y);
                    if ((byte)0 != cellValue)
                    {
                        pileHeight = y;
                    }
                }
            }
            //pileHeight = board.GetPileMaxHeight( );

            int holes = 0;
            int blocked = 0;
            for (int x = 1; x <= width; x++)
            {
                blocked = 0;
                for (int y = height; y >= 1; y--) // Top-to-Bottom
                {
                    // If line is complete, skip it!
                    if (width == lineCellTotal[(y - 1)])
                    {
                        clears++;
                        continue;
                    }
                    if (board.GetCell(x, y) > 0)
                    {
                        blocked = 1;  // We encountered an occupied cell; all below is blocked
                    }
                    else
                    {
                        // All of the following is in the context of the cell ( x, y )
                        // being UN-occupied.

                        // If any upper row had an occupied cell in this column, this
                        // unoccupied cell is considered blocked.
                        if (0 != blocked)
                        {
                            holes++; // This unoccupied cell is buried; it's a hole.
                        }
                    }
                }
            }

          

            rating = ((-1.0) * Constatnts[0] * (double)pileHeight) + (-1.0) * Constatnts[1] * holes + (pileHeight * bottomTouch) * Constatnts[2] + wallTouch * Constatnts[3] + blockTouch * Constatnts[4]  +clears  *  Constatnts[5] ;
        }

        bool isForeignBlock(int c_x, int c_y, int[,] blockCoordinates)
        {
            for (int i = 0; i < blockCoordinates.GetLength(0); i++)
            {
                if ((blockCoordinates[i, 0] == c_x) && (blockCoordinates[i, 1] == c_y))
                    return false;
            }
            return true ;
        }







    }
}
